package com.personal.dataconvert;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.personal.core.htmldata.HtmlIrregularData;
import com.personal.core.utils.CoreUtil;
import com.personal.core.utils.ReGularUtil;
import com.personal.dataconvert.bean.HeaderConfig;
import com.personal.dataconvert.port.Data2Excel;
import com.personal.dataconvert.util.ExcelHtmlUtil;

/**
 * 不规则单元数据转Excel
 * @author cuibo
 */
public class HtmlIrregularData2Excel implements Data2Excel
{
	/** 数据集合 */
	private Map<String, List<HtmlIrregularData>> htmlIrregularDatas;

	// Excel类型
	private String excelType;

	// workBook
	private Workbook workbook;

	/** 重新计算宽度（html宽度放大至Excel） */
	private boolean recalWidth = false;

	// 居中样式
	private CellStyle dataCellStyleCenter = null;

	// 左对其样式
	private CellStyle dataCellStyleLeft = null;

	// 右对齐样式
	private CellStyle dataCellStyleRight = null;

	/** 样式缓存 */
	private Map<String, CellStyle> cellStyleCache = new HashMap<String, CellStyle>();

	private HtmlIrregularData2Excel()
	{
		super();
	}

	private HtmlIrregularData2Excel(Builder builder)
	{
		this.excelType = builder.excelType;
		this.workbook = builder.workbook;
		this.recalWidth = builder.recalWidth;
		this.htmlIrregularDatas = builder.htmlIrregularDatas;
	}

	@Override
	public byte[] exportExcel() throws Exception
	{
		fillExcel();
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		workbook.write(out);
		return out.toByteArray();
	}

	@Override
	public Workbook fillExcel() throws Exception
	{
		if (htmlIrregularDatas == null || htmlIrregularDatas.isEmpty())
		{
			return workbook;
		}
		// 初始化样式
		init();
		for (Entry<String, List<HtmlIrregularData>> entry : htmlIrregularDatas.entrySet())
		{
			filllExcelImpl(entry.getKey(), entry.getValue());
		}
		return workbook;
	}

	/**
	 * 填充实现
	 * @param sheetName
	 * @param sheetDatas
	 */
	private void filllExcelImpl(String sheetName, List<HtmlIrregularData> sheetDatas)
	{
		if (CoreUtil.isEmpty(sheetName))
		{
			return;
		}
		sheetName = ExcelHtmlUtil.handleExcelSheetName(sheetName);
		// 创建Sheet页
		Sheet sheet = workbook.getSheet(sheetName);
		if (sheet == null)
		{
			sheet = workbook.createSheet(sheetName);
		}
		if (sheetDatas == null || sheetDatas.isEmpty())
		{
			return;
		}
		int rowIndex = 0;
		int colIndex = 0;
		// 新建的行
		Row sheetRow = null;
		// 新建Cell
		Cell cell = null;
		// 宽度缓存
		Map<Integer, Integer> widthMap = new HashMap<Integer, Integer>();

		// cb2017 06 23 考虑追加的情况
		int lastRowNum = 0;
		if (sheet.getPhysicalNumberOfRows() > 0)
		{
			lastRowNum = sheet.getLastRowNum() + 1;
		}
		else
		{
			lastRowNum = sheet.getLastRowNum();
		}
		for (HtmlIrregularData data : sheetDatas)
		{
			rowIndex = data.getRowIndex();
			rowIndex += lastRowNum;
			colIndex = data.getColIndex();
			sheetRow = sheet.getRow(rowIndex);
			if (sheetRow == null)
			{
				sheetRow = sheet.createRow(rowIndex);
			}
			cell = sheetRow.getCell(colIndex);
			if (cell == null)
			{
				cell = sheetRow.createCell(colIndex);
			}
			if (CoreUtil.isNumber(data.getData()) && ReGularUtil.NUMBER_PATTERN.matcher(data.getData()).matches())
			{
				cell.setCellValue(CoreUtil.parseDbl(data.getData()));
				cell.setCellType(Cell.CELL_TYPE_NUMERIC);
			}
			else
			{
				cell.setCellValue(data.getData());
			}
			// 设置样式
			cell.setCellStyle(getCellStyle(data));
			// 宽度:取最小的宽度
			if (!widthMap.containsKey(data.getColIndex()) || widthMap.get(data.getColIndex()) > data.getWidth())
			{
				widthMap.put(data.getColIndex(), data.getWidth());
			}
			// 设置高度
			sheetRow.setHeightInPoints(data.getHeight() - 8);
		}
		// 合并单元格
		CellRangeAddress cra = null;
		int rowSpan = 0;
		int colSpan = 0;
		for (HtmlIrregularData data : sheetDatas)
		{
			rowSpan = data.getRowSpan();
			colSpan = data.getColSpan();
			// 调整
			rowIndex = data.getRowIndex();
			rowIndex += lastRowNum;
			colIndex = data.getColIndex();
			if (rowSpan > 1 || colSpan > 1)
			{
				cra = new CellRangeAddress(rowIndex, rowIndex + rowSpan - 1, colIndex, colIndex + colSpan - 1);
				sheet.addMergedRegion(cra);
				setBodyStyle(getCellStyle(data), cra, sheet);
			}
		}
		// 设置列宽
		setColWidth(sheet, widthMap);
	}

	private void setColWidth(Sheet sheet, Map<Integer, Integer> widthMap)
	{
		if (widthMap.isEmpty())
		{
			return;
		}
		for (Entry<Integer, Integer> entry : widthMap.entrySet())
		{
			sheet.setColumnWidth(entry.getKey(), recalWidth ? entry.getValue() * ExcelHtmlUtil.RECALWIDTHRATIO : entry.getValue());
		}
	}

	/**
	 * 获得样式
	 * @param cloneStyle
	 * @param cra
	 * @param sheet
	 */
	private void setBodyStyle(CellStyle cloneStyle, CellRangeAddress cra, Sheet sheet)
	{
		Row rowtemp = null;
		Cell celltemp = null;
		for (int i = cra.getFirstRow(); i <= cra.getLastRow(); i++)
		{
			rowtemp = CellUtil.getRow(i, sheet);
			for (int j = cra.getFirstColumn(); j <= cra.getLastColumn(); j++)
			{
				celltemp = rowtemp.getCell(j);
				if (celltemp == null)
				{
					celltemp = rowtemp.createCell(j);
				}
				celltemp.setCellStyle(cloneStyle);
			}
		}
	}

	/**
	 * 获取其对应的样式
	 * @param data
	 * @return
	 */
	private CellStyle getCellStyle(HtmlIrregularData data)
	{
		if (data == null)
		{
			return dataCellStyleCenter;
		}
		// 水平方式
		String align = data.getAlign();
		// 垂直方式
		String valign = data.getValign();
		if (CoreUtil.isEmpty(valign))
		{
			align = HeaderConfig.CENTER;
		}
		if (CoreUtil.isEmpty(valign))
		{
			valign = HeaderConfig.CENTER;
		}
		if (HeaderConfig.CENTER.equals(valign))
		{
			// 默认垂直方向居中
			if (HeaderConfig.LEFT.equals(align))
			{
				return dataCellStyleLeft;
			}
			else if (HeaderConfig.RIGHT.equals(align))
			{
				return dataCellStyleRight;
			}
			else
			{
				return dataCellStyleCenter;
			}
		}
		else
		{
			if (cellStyleCache.containsKey(align + valign))
			{
				return cellStyleCache.get(align + valign);
			}
			if (HeaderConfig.LEFT.equals(align))
			{
				CellStyle copyStyle = workbook.createCellStyle();
				copyStyle.cloneStyleFrom(dataCellStyleLeft);
				copyStyle.setVerticalAlignment(convertVerticalAlignToExcel(valign));
				cellStyleCache.put(align + valign, copyStyle);
				return copyStyle;
			}
			else if (HeaderConfig.RIGHT.equals(align))
			{
				CellStyle copyStyle = workbook.createCellStyle();
				copyStyle.cloneStyleFrom(dataCellStyleRight);
				copyStyle.setVerticalAlignment(convertVerticalAlignToExcel(valign));
				cellStyleCache.put(align + valign, copyStyle);
				return copyStyle;
			}
			else
			{
				CellStyle copyStyle = workbook.createCellStyle();
				copyStyle.cloneStyleFrom(dataCellStyleCenter);
				copyStyle.setVerticalAlignment(convertVerticalAlignToExcel(valign));
				cellStyleCache.put(align + valign, copyStyle);
				return copyStyle;
			}
		}
	}

	private void init()
	{
		if (workbook == null)
		{
			if (ExportData2Excel.XLSX.equals(excelType))
			{
				workbook = new XSSFWorkbook();
			}
			else
			{
				workbook = new HSSFWorkbook();
			}
		}
		initCellStyle();
	}
	
	 /**
     * 返回Excel的垂直样式
     * @param verticalAlignment
     * @return
     */
    private short convertVerticalAlignToExcel(String verticalAlignment)
    {
        short result = CellStyle.VERTICAL_CENTER;
        if ("bottom".equals(verticalAlignment))
        {
            result = CellStyle.VERTICAL_BOTTOM;
        } else if ("top".equals(verticalAlignment))
        {
            result = CellStyle.VERTICAL_TOP;
        } else
        {
            result = CellStyle.VERTICAL_CENTER;
        }
        return result;
    }

	private void initCellStyle()
	{
		dataCellStyleLeft = workbook.createCellStyle();
		dataCellStyleLeft.setAlignment(CellStyle.ALIGN_LEFT);

		// ==============增加边框================
		dataCellStyleLeft.setBorderTop(CellStyle.BORDER_THIN);
		dataCellStyleLeft.setBorderLeft(CellStyle.BORDER_THIN);
		dataCellStyleLeft.setBorderBottom(CellStyle.BORDER_THIN);
		dataCellStyleLeft.setBorderRight(CellStyle.BORDER_THIN);
		dataCellStyleLeft.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
		dataCellStyleLeft.setWrapText(true);

		dataCellStyleCenter = workbook.createCellStyle();
		dataCellStyleCenter.setAlignment(CellStyle.ALIGN_CENTER);
		// ==============增加边框================
		dataCellStyleCenter.setBorderTop(CellStyle.BORDER_THIN);
		dataCellStyleCenter.setBorderLeft(CellStyle.BORDER_THIN);
		dataCellStyleCenter.setBorderBottom(CellStyle.BORDER_THIN);
		dataCellStyleCenter.setBorderRight(CellStyle.BORDER_THIN);
		dataCellStyleCenter.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
		dataCellStyleCenter.setWrapText(true);

		dataCellStyleRight = workbook.createCellStyle();
		dataCellStyleRight.setAlignment(CellStyle.ALIGN_RIGHT);
		// ==============增加边框================
		dataCellStyleRight.setBorderTop(CellStyle.BORDER_THIN);
		dataCellStyleRight.setBorderLeft(CellStyle.BORDER_THIN);
		dataCellStyleRight.setBorderBottom(CellStyle.BORDER_THIN);
		dataCellStyleRight.setBorderRight(CellStyle.BORDER_THIN);
		dataCellStyleRight.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
		dataCellStyleRight.setWrapText(true);

	}

	/**
	 * 建造
	 * @author cuibo
	 */
	public static class Builder
	{
		private Map<String, List<HtmlIrregularData>> htmlIrregularDatas;
		private String excelType;
		private Workbook workbook;
		private boolean recalWidth = false;

		public Builder setExcelType(String excelType)
		{
			this.excelType = excelType;
			return this;
		}

		public Builder setWorkbook(Workbook workbook)
		{
			this.workbook = workbook;
			return this;
		}

		public Builder setRecalWidth(boolean recalWidth)
		{
			this.recalWidth = recalWidth;
			return this;
		}

		public Builder setHtmlIrregularDatas(Map<String, List<HtmlIrregularData>> htmlIrregularDatas)
		{
			this.htmlIrregularDatas = htmlIrregularDatas;
			return this;
		}
		
		public Builder addHtmlIrregularData(String sheetName, List<HtmlIrregularData> htmlIrregularDatas)
		{
			if (this.htmlIrregularDatas == null) 
			{
				this.htmlIrregularDatas = new LinkedHashMap<String, List<HtmlIrregularData>>();
			}
			this.htmlIrregularDatas.put(sheetName, htmlIrregularDatas);
			return this;
		}
		
		public HtmlIrregularData2Excel build()
		{
			return new HtmlIrregularData2Excel(this);
		}
	}

}
